import xml.etree.ElementTree as ET
import os
import numpy as np
from object_detection.utils import label_map_util

from .basic import check_consistent_length


def get_label_from_pd_file(pd_file, class_num):
    """
    read .pdtxt file and return the category index
    :param pd_file:
    :param class_num:
    :return: category index, like:
                            {1: {'id': 1, 'name': 'fisheye'},
                             2: {'id': 2, 'name': 'person'},
                             ...}
    """
    label_map = label_map_util.load_labelmap(pd_file)
    categories = label_map_util.convert_label_map_to_categories(
        label_map, max_num_classes=class_num, use_display_name=True)
    category_index = label_map_util.create_category_index(categories)
    return category_index


def get_label_list_from_category_index(category_index=None, pd_file='', class_num=0):
    label_list = []
    if pd_file != '':
        category_index = get_label_from_pd_file(pd_file, class_num)
    for idx in range(len(category_index)):
        item = category_index[idx + 1]
        label_list.append(item['name'])
    return label_list


def read_voc_xml(file_path, image_size, label_list):
    """
    read the xml file and extract the objects information
    the xml file must be organized as PASCAL VOC xml file
    :param file_path:
    :param image_size:
    :param label_list:
    :return:
    """
    tree = ET.parse(file_path.strip())
    root = tree.getroot()
    size = root.find('size')
    width = int(size.find('width').text)
    height = int(size.find('height').text)
    break_instance = True
    objects = []

    def _read_xml(changed=False):
        for obj in root.iter('object'):
            cls_name = obj.find('name').text
            if len(label_list) > 0:
                if cls_name not in label_list:
                    continue
            xml_box = obj.find('bndbox')
            xmin = int(xml_box.find('xmin').text)
            ymin = int(xml_box.find('ymin').text)
            xmax = int(xml_box.find('xmax').text)
            ymax = int(xml_box.find('ymax').text)
            if changed:
                xmin, ymin, xmax, ymax = ymin, xmin, ymax, xmax
            if (0 <= xmin < xmax <= image_size[1]) or (
                    0 <= ymin < ymax <= image_size[0]):
                objects.append([cls_name, xmin, ymin, xmax, ymax])

    if (image_size[0] == height) and (image_size[1] == width):
        _read_xml()
        break_instance = False
    elif (image_size[0] == width) and (image_size[1] == height):
        _read_xml(True)
        break_instance = False
    return objects, break_instance


def read_text_file(file_name, encoding='rt'):
    """
    按行读取文本文件

    Parameters
    ----------
    file_name: file name
    encoding:

    Returns
    -------
        list of the content in file
    """
    with open(file_name, encoding) as file:
        list = []
        for line in file.readlines():
            line = line.strip('\n')
            list.append(line)
        return list#file.readlines()      #readlines() 方法用于读取所有行(直到结束符 EOF)并返回列表


def write_text_file(file_name, *file_lists, split=' ', encoding='w'):
    """
    将多个字符串列表写入文本文件中

    Parameters
    ----------
    file_name
    file_lists
    split
    encoding

    Returns
    -------

    """
    check_consistent_length(*file_lists)
    with open(file_name, encoding) as file:
        for strings in zip(*file_lists):
            string = split.join(strings)
            file.write(string+'\n')


def read_label_as_list(file_path, classes=9, instance=0, split='&!&'):
    """
    read a file which has storage the label and instance number
    :param file_path: file name, such as label.txt:
                                             1000[split]class1
                                             900[split]class2
                                             800[split]class3
                                             ...
    :param classes: number of class required
    :param instance: filtering all the class which has less instance
                      than [instance]
    :param split: a delimiter between instance and class name
    :return: list of class names
    """
    array = read_text_file(file_path)
    array = np.array(list(map(lambda x: x.strip().split(split), array)))
    index = list(map(lambda x: int(x), array[:, 0]))
    name = list(array[:, 1])
    assert classes <= len(index), 'required classes is bigger than actual !'
    assert instance <= index[0], 'required instance is bigger than actual !'
    if instance == 0:
        return name[:classes]
    else:
        for idx, value in enumerate(index):
            if value < instance:
                return name[:idx]
        return name


def read_label_as_map_dict(file_path, classes=9, instance=0, split='&!&'):
    """
    read a file which has storage the label and instance number
    :param file_path: see [read_label_as_list]
    :param classes: see [read_label_as_list]
    :param instance: see [read_label_as_list]
    :param split: see [read_label_as_list]
    :return: a category index see [get_label_from_pd_file]
    """
    label_list = read_label_as_list(file_path, classes, instance, split)
    map_dict = {}
    for idx, label in enumerate(label_list):
        map_dict[idx + 1] = {'id': idx + 1, 'name': label}
    return map_dict


def create_map_pdtxt(file_path, label_list):
    """
    create a .pdtxt file from [label_list]
    :param file_path: a file name of .pdtxt file
    :param label_list:
    :return:
    """
    base_str = """item {}\n  id: {}\n  name: '{}'\n{}\n"""
    string = []
    for idx, label in enumerate(label_list):
        string.append(base_str.format('{', idx + 1, label, '}'))
    write_text_file(file_path, string)


if __name__ == '__main__':
    file_ = '/home/admins/repos/hightensity/hightensity/335/335_70472017-06-04.png.dat'
    lll = read_text_file(file_)
    print(lll)
